from multiprocessing import Queue

from stream.pull import StreamPuller
from stream.push import ImagePusher, ResultsPusher
from detect.pool import DetecterPool
from detect.drawer import Drawer
from alert.alerter import Alerter
from logger import logger
from config import config
from utils import register_exit


error_queue = Queue()  # 异常处理队列，如果这个队列里有东西，说明程序异常，需要退出了
processes = None

def quit():
    # 先调用terminate再调kill的原因是：
    # terminate让子进程注册自己的退出函数，做退出处理
    # kill就大有说法了，仅凭terminate在子进程内调用exit()会产生僵尸进程
    #   原因是消息队列Queue中有数据残留，就导致子进程无法完全退出
    #   我试了很多方法，都没有办法在注册的退出函数里将残留数据取出（可能是因为在这之前创建Queue的主进程已经exit了）
    #   最后尝试了最简单的办法就是kill，不然要改的东西就太多了
    if processes:
        [process.terminate() for process in processes]
        [process.kill() for process in processes]

def init():
    register_exit(quit)

    logger.info(f'启动参数：{config}')

    if config.enable_draw_video_box:
        logger.info('已启用绘制视频画面识别框')

    if config.enable_draw_alert_box:
        logger.info('已启用绘制报警图像识别框')

    config.multiprocess = config.enable_multiprocess and config.mutiprocess_worker_num > 1
    Drawer.init(config)

    if config.multiprocess:
        logger.info(f'已开启多进程检测，进程数量：{config.mutiprocess_worker_num}')
        puller = StreamPuller(config, error_queue)  # 有问题的话，构造函数最多会卡5s
        pool = DetecterPool(config, puller.stream_queue, error_queue)
        pusher = ImagePusher(config, pool.pusher_queue, error_queue)
    else:
        logger.info(f'未开启多进程检测，即将进行单进程补帧检测')
        puller = StreamPuller(config, error_queue, True)  # 有问题的话，构造函数最多会卡5s
        pool = DetecterPool(config, puller.stream_queue, error_queue)
        pusher = ResultsPusher(config, pool.pusher_queue, puller.aux_stream_queue, error_queue)
    alerter = Alerter(config, pool.alerter_queue)
    return puller, pool, pusher, alerter

try:
    processes = init()
except Exception as e:  # 初始化所有子进程遇到异常了
    if error_queue.qsize():
        logger.fatal(error_queue.get())  # 子进程也有报错
    logger.fatal(str(e))
else:  # 初始化成功
    logger.fatal(error_queue.get())  # 如果没报错，那就主进程这里就会卡在这
quit()
